﻿using Microsoft.Extensions.DependencyInjection;

using Newtonsoft.Json.Linq;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Runtime.ConstrainedExecution;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

using Utils.Weixin.Pay.Http;
using Utils.Weixin.Pay.Model;
using Utils.Weixin.Pay.Options;
using Utils.Weixin.Pay.Result;
using Utils.Weixin.Pay.Helper;
using System.Diagnostics.Contracts;
using System.IO;
using System.Net.Http.Headers;
using Newtonsoft.Json;
using System.ComponentModel.DataAnnotations;
using System.Collections;
using Microsoft.Extensions.Logging;

namespace Utils.Weixin.Pay
{
    public class WeChatPartnerPay:IDisposable
    {
        private readonly WeChatPayOptions options;

        private readonly HttpClient httpClient;

        private const string BASE_URL = "https://api.mch.weixin.qq.com/";

        private readonly IServiceProvider serviceProvider;

        private readonly ICacheStrategy cacheStrategy;

        public string MerchantNo => options.MerchantNo;

        public WeChatPartnerPay(WeChatPayOptions options, IServiceProvider serviceProvider, ICacheStrategy cacheStrategy)
        {
            if (options.PrivateKey.StartsWith("-----BEGIN PRIVATE KEY-----"))
            {
                options.PrivateKey = RSAFromPkcs8.ExtractFromPemFormat(options.PrivateKey);
            }
            this.serviceProvider = serviceProvider;
            this.options = options;
            this.cacheStrategy = cacheStrategy.Change(6);
            this.httpClient = new HttpClient(new HttpApiV3Handler(options))
            {
                BaseAddress = new Uri(BASE_URL)
            };
        }

        public WeChatPay CreateWeChatPay(WeChatPayOptions chatPayOptions)
        {
            if (chatPayOptions.IsSub)
            {
                return new WeChatPayForOpen(options, chatPayOptions, serviceProvider.GetService<ILogger<WeChatPayForOpen>>(), cacheStrategy);
            }
            return ActivatorUtilities.CreateInstance<WeChatPay>(serviceProvider, chatPayOptions);
        }


        /// <summary>
        /// 获取证书列表
        /// </summary>
        /// <returns></returns>
        public async Task<List<CertificateResult>> GetCertificates()
        {
            var result = await httpClient.GetAsync("v3/certificates").ReadAsWxPayAsync<JObject>();
            var arr = (JArray)result["data"];
            return arr.Select(a => Decrypt(a)).ToList();
        }


        private CertificateResult Decrypt(JToken token)
        {
            var cert = token.ToObject<CertificateResult>();
            var encryptCertificate = token["encrypt_certificate"];
            //var algorithm = encryptCertificate["algorithm"].ToString();
            var associatedData = encryptCertificate["associated_data"].ToString();
            var ciphertext = encryptCertificate["ciphertext"].ToString();
            var nonce = encryptCertificate["nonce"].ToString();
            string certificateText = Security.AESHelper.AesGcmDecrypt(options.ApiV3, associatedData, nonce, ciphertext);
            cert.CertificateText = certificateText;
            return cert;
        }

        public async Task<string> Upload(string fileName, Stream stream)
        {
            var multipart = new MultipartFormDataContent();
            var bytes = stream.ToBytes();
            var sha256 = SHA256.Create().ComputeHash(bytes).ToHashString();
            multipart.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
            multipart.Add(new JsonContent(new { filename = fileName, sha256 = sha256 }), "\"meta\"");
            var byteArray = new ByteArrayContent(bytes);
            byteArray.Headers.ContentType = MediaTypeHeaderValue.Parse("image/jpeg");
            multipart.Add(byteArray, "\"file\"", "\"" + fileName + "\"");
            var request = new HttpRequestMessage()
            {
                RequestUri = new Uri(BASE_URL + "v3/merchant/media/upload"),
                Method = HttpMethod.Post,
                Content = multipart
            };
            var jtoken = await httpClient.SendAsync(request).ReadAsWxPayAsync<JObject>();
            return jtoken["media_id"].ToString();
        }


        public async Task<string> Applyment(string applyNo, JObject data)
        {

            string miniProgramAppid = data["miniProgramAppid"]?.ToString();
            if (miniProgramAppid.IsNullOrEmpty())
            {
                throw Error.DefaultError("请先授权小程序");
            }
            var encrypter = GetEncrypter();
            PersonInfo contact = data.ToObject<PersonInfo>();
            BusinessInfo business = data.ToObject<BusinessInfo>();
            BankInfo bank = data.ToObject<BankInfo>();
            VerifyUtil.Verify(contact).ValidateObject().ThrowInputError();
            VerifyUtil.Verify(business).ValidateObject().ThrowInputError();
            VerifyUtil.Verify(bank).ValidateObject().ThrowInputError();

            var request = new HttpRequestMessage()
            {
                RequestUri = new Uri(BASE_URL + "v3/applyment4sub/applyment/"),
                Method = HttpMethod.Post,
                Content = new JsonContent(new
                {
                    business_code = applyNo,
                    contact_info = new
                    {
                        contact_type = "LEGAL", // LEGAL：经营者/法人 SUPER：经办人
                        contact_name = encrypter.Encrypt(contact.RealName),
                        contact_id_doc_type = "IDENTIFICATION_TYPE_IDCARD",
                        //contact_id_number = encrypter.Encrypt(contact.IdCardNo),
                        //contact_id_doc_copy = contact.IdCardPortrait,
                        //contact_id_doc_copy_back = contact.IdCardNational,
                        //contact_period_begin = contact.CardValidBegin.FormatString("yyyy-MM-dd"), // YYYY-MM-DD
                        //contact_period_end = contact.CardValidEnd?.FormatString("yyyy-MM-dd") ?? "长期",
                        business_authorization_letter = "", // 当超级管理员非法人，上传
                        openid = contact.OpenId,
                        mobile_phone = encrypter.Encrypt(contact.Mobile),
                        contact_email = encrypter.Encrypt(contact.Email),
                    },
                    subject_info = new
                    {
                        // SUBJECT_TYPE_INDIVIDUAL 个体工商户  SUBJECT_TYPE_ENTERPRISE 有限公司
                        subject_type = business.BusinessType == 1 ? "SUBJECT_TYPE_INDIVIDUAL" : "SUBJECT_TYPE_ENTERPRISE",
                        business_license_info = new
                        {
                            license_copy = business.BusinessLicense,
                            license_number = business.LicenseNumber,
                            merchant_name = business.MerchantName,
                            legal_person = contact.RealName,
                            license_address = business.LicenseAddress,
                            period_begin = business.ValidBegin?.FormatString("yyyy-MM-dd"),
                            period_end = business.ValidEnd?.FormatString("yyyy-MM-dd"),
                        },
                        identity_info = new
                        {
                            id_holder_type = "LEGAL",
                            id_doc_type = "IDENTIFICATION_TYPE_IDCARD",
                            id_card_info = new
                            {
                                id_card_copy = contact.IdCardPortrait,
                                id_card_national = contact.IdCardNational,
                                id_card_name = encrypter.Encrypt(contact.RealName),
                                id_card_number = encrypter.Encrypt(contact.IdCardNo),
                                id_card_address = contact.IdCardAddress.IsNullOrEmpty() ? null : encrypter.Encrypt(contact.IdCardAddress),
                                card_period_begin = contact.CardValidBegin.FormatString("yyyy-MM-dd"),
                                card_period_end = contact.CardValidEnd?.FormatString("yyyy-MM-dd") ?? "长期",
                            },
                            owner = true // 经营者/法人是否为受益人
                        }
                    },
                    business_info = new
                    {
                        merchant_shortname = business.ShortMerchantName,
                        service_phone = business.ServicePhone.IfNullOrEmpty(contact.Mobile),
                        sales_info = new
                        {
                            sales_scenes_type = new[] { "SALES_SCENES_MINI_PROGRAM" },
                            mini_program_info = new
                            {
                                mini_program_sub_appid = miniProgramAppid,
                            }
                        }
                    },
                    settlement_info = new
                    {
                        settlement_id = business.BusinessType == 2 ? "716" : "719",
                        qualification_type = business.Industry,
                        qualifications = business.Qualifications,
                    },
                    bank_account_info = new
                    {
                        bank_account_type = "BANK_ACCOUNT_TYPE_CORPORATE",
                        account_number = encrypter.Encrypt(bank.AccountNumber), // 卡号
                        account_bank = bank.AccountBank, // 开户银行
                        account_name = encrypter.Encrypt(bank.AccountName),
                        bank_address_code = bank.BankAddressCode,
                        bank_name = bank.BankName,
                    },
                    addition_info = new { },
                })
            };
            request.Headers.Add("Wechatpay-Serial", encrypter.SerialNo);
            var jtoken = await httpClient.SendAsync(request).ReadAsWxPayAsync<JObject>();
            return jtoken["applyment_id"]?.ToString();
        }

        /// <summary>
        /// 查询状态
        /// </summary>
        /// <param name="applymentId"></param>
        /// <returns></returns>
        public async Task<JObject> ApplymentStatus(string applymentId)
        {
            return await httpClient.GetAsync($"v3/applyment4sub/applyment/applyment_id/{applymentId}/").ReadAsWxPayAsync<JObject>();
        }

        /// <summary>
        /// 卡号搜索个人业务银行
        /// </summary>
        /// <param name="accountNumber"></param>
        /// <returns></returns>
        public async Task<PageResult<BankResult>> SearchBanks(string accountNumber)
        {
            var encrypter = GetEncrypter();
            var request = new HttpRequestMessage()
            {
                RequestUri = new Uri(BASE_URL + $"v3/capital/capitallhh/banks/search-banks-by-bank-account?account_number={encrypter.Encrypt(accountNumber)}"),
                Method = HttpMethod.Get,
            };
            return await httpClient.SendAsync(request)
                .ReadAsWxPayAsync<PageResult<BankResult>>();
        }


        public async Task<PageResult<BankResult>> PersonalBanking(int offset, int limit)
        {
            return await httpClient.GetAsync($"v3/capital/capitallhh/banks/personal-banking?offset={offset}&limit={limit}")
                .ReadAsWxPayAsync<PageResult<BankResult>>();
        }

        /// <summary>
        /// 对公卡号
        /// </summary>
        /// <returns></returns>
        public async Task<PageResult<BankResult>> CorporateBanking(int offset, int limit)
        {
            return await httpClient.GetAsync($"v3/capital/capitallhh/banks/corporate-banking?offset={offset}&limit={limit}")
                .ReadAsWxPayAsync<PageResult<BankResult>>();
        }
        /// <summary>
        /// 查询支行
        /// </summary>
        /// <param name="bankAliasCode"></param>
        /// <param name="cityCode"></param>
        /// <param name="offset"></param>
        /// <param name="limit"></param>
        /// <returns></returns>
        public async Task<PageResult<BranchResult>> Branches(string bankAliasCode, string cityCode, int offset, int limit)
        {
            return await httpClient.GetAsync($"v3/capital/capitallhh/banks/{bankAliasCode}/branches?city_code={cityCode}offset={offset}&limit={limit}")
             .ReadAsWxPayAsync<PageResult<BranchResult>>();
        }

        public async Task<JArray> Provinces()
        {
            var obj = await httpClient.GetAsync($"v3/capital/capitallhh/areas/provinces")
             .ReadAsWxPayAsync<JObject>();
            return (JArray)obj["data"];
        }

        public async Task<JArray> Cities(string provinceCode)
        {
            var obj = await httpClient.GetAsync($"v3/capital/capitallhh/areas/provinces/{provinceCode}/cities")
             .ReadAsWxPayAsync<JObject>();
            return (JArray)obj["data"];
        }

        private Encrypter GetEncrypter()
        {
            string key = "pay:cert:" + options.MerchantNo;
            lock (this)
            {
                var certResult = cacheStrategy.Get<CertificateResult>(key);
                if (certResult == null)
                {
                    var certs = GetCertificates().GetAwaiter().GetResult();
                    certResult = certs.OrderByDescending(a => a.ExpireTime).FirstOrDefault();
                    if (certResult == null)
                        throw Error.DefaultError("下载证书失败");
                    cacheStrategy.Add(key, certResult, (int)((certResult.ExpireTime - DateTime.Now).TotalSeconds - 3600));
                }
                return new Encrypter(certResult);
            }
        }

        public void Dispose()
        {
            cacheStrategy.CloseChange();
            httpClient.Dispose();
        }
    }
}
